#include "stdafx.h"

#include "Menu.h"
#include "Application.h"
#include "EventHandlers.h"
#include "EventHandlersFactory.h"

#include <HSModel/GameController.h>
#include <HSModel/SharedConstants.h>
#include <HSModel/GameConstants.h>
#include <HSModel/WorldDumper.h>
#include <HSModel/WorldUpdater.h>

#include <string>


namespace
  {
  void _RunThrought(double& io_min_x, double& io_min_y, double& io_max_x, double& io_max_y, CEGUI::Window* i_root)
    {
    for(size_t id = 0; id < i_root->getChildCount(); ++id)
      {
      if(i_root->getChildAtIdx(id)->getChildCount() != 0)
        _RunThrought(io_min_x, io_min_y, io_max_x, io_max_y, i_root->getChildAtIdx(id));
      else
        {
        double min_x = i_root->getChildAtIdx(id)->getClipRect().getPosition().d_x;
        double min_y = i_root->getChildAtIdx(id)->getClipRect().getPosition().d_y;
        double max_x = i_root->getChildAtIdx(id)->getClipRect().getPosition().d_x + i_root->getChildAtIdx(id)->getClipRect().getWidth();
        double max_y = i_root->getChildAtIdx(id)->getClipRect().getPosition().d_y + i_root->getChildAtIdx(id)->getClipRect().getHeight();
        
        if(min_x < io_min_x)
          io_min_x = min_x;
        if(min_y < io_min_y)
          io_min_y = min_y;

        if(max_x > io_max_x)
          io_max_x = max_x;
        if(max_y > io_max_y)
          io_max_y = max_y;
        }
      }
    }
	void GetTargetFileString(std::string& o_filename, int i_file_index)
		{
		switch (i_file_index) 
			{
			case 1 : 
				o_filename = "Saves/Slot1.xml";
				break;
			case 2 : 
				o_filename = "Saves/Slot2.xml";
				break;
			case 3 :
				o_filename = "Saves/Slot3.xml";
				break;
			default :
					assert(false && "not valid target save folder");
  }
		}
		
	std::string GetFileDate(const std::string& i_filename)
		{
		WIN32_FIND_DATA FindFileData;
		std::string answer;
		HANDLE hFind = FindFirstFile(i_filename.c_str(), &FindFileData);
		if (hFind == INVALID_HANDLE_VALUE) 
			{
			return answer;
	}
		FILETIME file_time = FindFileData.ftLastWriteTime;
		SYSTEMTIME time;
		FileTimeToSystemTime(&file_time, &time);
		SYSTEMTIME local_time;
		SystemTimeToTzSpecificLocalTime(NULL, &time, &local_time);
		answer += std::to_string(local_time.wHour) + ":" + std::to_string(local_time.wMinute) + " ";
		answer += std::to_string(local_time.wDay) + "." + std::to_string(local_time.wMonth) + "." + std::to_string(local_time.wYear);
		FindClose(hFind);
		return answer;
		}
	}

// ------

ButtonInfo::ButtonInfo()
  {

  }

ButtonInfo::ButtonInfo(const std::string &i_button_name, const std::string &i_button_normal, const std::string &i_button_active)
  :handler(i_button_name),
  button_normal(i_button_normal),
  button_active(i_button_active)
  {
  }

// ------

bool Menu::_CEGUI_HandleStateButtonClick(const CEGUI::EventArgs &args)
  {
  auto w_args = static_cast<const CEGUI::WindowEventArgs&>(args);

  std::cout << w_args.window->getName() << " pressed" << std::endl;

  _HandleStateChange(w_args.window->getName());
  return true;
  }

void Menu::_HandleStateChange(const CEGUI::String &i_button_name)
  {
  ResetButtonCaptions();
  ButtonInfo btn = m_buttons_map[i_button_name];

  if(!m_app.IsEventHandler(btn.handler))
  {
    _SetButtonCaption(i_button_name, btn.button_active);
    m_app.ResetHandler(EventHandlersFactory(m_app).GetInstance(btn.handler));
  }
  else
    {
    m_app.ResetHandler();
    }
  }

void Menu::_CEGUI_SaveWorld(int i_target_save)
  {
  std::cout << "Save world pressed" << std::endl;
	std::string saving_string;
	GetTargetFileString(saving_string, i_target_save);
	WorldDumper dumper(m_app.GetWorld(), m_app.GetGameController(), saving_string);
  dumper.Dump();
	_SetButtonCaption("LoadWorldButton" + std::to_string(i_target_save),  GetFileDate(saving_string));
  }

void Menu::_CEGUI_LoadWorld(int i_target_load)
  {
  std::cout << "Load world pressed" << std::endl;
	std::string loading_string;
	GetTargetFileString(loading_string, i_target_load);
	WorldDumper dumper(m_app.GetWorld(), m_app.GetGameController(), loading_string);
  dumper.Restore();
  m_app.ResetPicker();
  m_app.ResetHandler();
  ResetButtonCaptions();
  m_app.GetWorld().GenerateChangeEvent();
  }

void Menu::_CEGUI_NewGame()
  {
	std::string load_str;
#ifdef NDEBUG
  load_str = "..\\..\\Source\\Resources\\Media\\CEGUISymbols.xml";
#else
  load_str = "..\\..\\Source\\Resources\\Media\\OgreSymbols.xml";
#endif
	WorldDumper dumper(m_app.GetWorld(), m_app.GetGameController(), load_str);
  dumper.Restore();
  m_app.ResetPicker();
  m_app.ResetHandler();
  ResetButtonCaptions();
  m_app.GetWorld().GenerateChangeEvent();
  }

Menu::Menu(Application& i_app, GameController& i_game_controller, bool i_visible)
  : m_app(i_app),
  m_game_controller(i_game_controller),
  m_is_visible(i_visible)
  {
  _Initialize();
  };

Menu::~Menu()
  {
  }

void Menu::_InitButtons()
  {
  m_buttons_map["CreateHillButton"] = ButtonInfo("CreateHill", "Create Hill", "Creating Hill...");
  m_buttons_map["CreateHoleButton"] = ButtonInfo("CreateHole", "Create Hole", "Creating Hole...");
  m_buttons_map["AddWalkwayButton"] = ButtonInfo("AddWalkway", "Add Walkway ($" + std::to_string(GameConstants::WALKWAY_COST) + ")", "Adding Walkway...");
  m_buttons_map["CreateTreeButton"] = ButtonInfo("AddTree", "Add Tree ($" + std::to_string(GameConstants::TREE_COST) + ")", "Adding Tree...");
  m_buttons_map["CreateWaterButton"] = ButtonInfo("CreateWater", "Add Water ($" + std::to_string(GameConstants::WATER_COST) + ")", "Adding Water...");
  m_buttons_map["CreateFerrisWheelButton"] = ButtonInfo("AddFerrisWheel", "Add Ferris Wheel ($" + std::to_string(GameConstants::FERRIS_WHEEL_COST) + ")", "Adding Ferris Wheel...");
  m_buttons_map["DestroyObjectsButton"] = ButtonInfo("DestroyObjects", "Destroy Objects (Refund: 20%)", "Destroying Objects...");
  m_buttons_map["AddGrassButton"] = ButtonInfo("AddGrass", "Add Grass ($" + std::to_string(GameConstants::GRASS_COST - GameConstants::WALKWAY_REFUND) + ")", "Adding Grass...");
  m_buttons_map["CreateBalloonsShopButton"] = ButtonInfo("AddBalloonsShop", "Add Balloons Shop ($" + std::to_string(GameConstants::BALLOONS_SHOP_COST) + ")", "Adding Balloons Shop...");
  m_buttons_map["AddBenchButton"] = ButtonInfo("AddBench", "Add Bench ($" + std::to_string(GameConstants::BENCH_COST) + ")", "Adding Bench...");

  auto p_root_window = CEGUI::System::getSingleton().getDefaultGUIContext().getRootWindow();

  for(auto button : m_buttons_map)
    {
    p_root_window->getChildElementRecursive(button.first)->
      subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&Menu::_CEGUI_HandleStateButtonClick, this));
    }
	const size_t NUMBER_OF_SAVE_AND_LOAD_BUTTONS = 3;

	for(int i = 1; i <= NUMBER_OF_SAVE_AND_LOAD_BUTTONS; ++i)
		{
		p_root_window->getChildElementRecursive("SaveWorldButton" + std::to_string(i))->
			subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(
				[this, i](const CEGUI::EventArgs&) 
				{
				_CEGUI_SaveWorld(i);
				return true;
				}));

		p_root_window->getChildElementRecursive("LoadWorldButton" + std::to_string(i))->
			subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(
				[this, i](const CEGUI::EventArgs&) 
				{
				_CEGUI_LoadWorld(i);
				return true;
				}));
		std::string target_file;
		GetTargetFileString(target_file, i);
		std::string date_string = GetFileDate(target_file);
		if (date_string.size() == 0)
			date_string = "Empty";
		_SetButtonCaption("LoadWorldButton" + std::to_string(i), date_string);
		}

  	p_root_window->getChildElementRecursive("NewGameButton")->subscribeEvent(
      CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber([this](const CEGUI::EventArgs&) 
				{
				_CEGUI_NewGame();
        return true;
				}
    ));

  ResetButtonCaptions();
  }

void Menu::_Initialize()
  {
  CEGUI::Window *p_root_window = CEGUI::WindowManager::getSingleton().loadLayoutFromFile("HSGUI.layout");
  CEGUI::System::getSingleton().getDefaultGUIContext().setRootWindow(p_root_window);

  _InitButtons();
  
  CEGUI::TabControl* tab_control = static_cast<CEGUI::TabControl*>(p_root_window->getChildElement("TabControl"));
  CEGUI::Window* tab_page1 = static_cast<CEGUI::Window*>(p_root_window->getChildElement("TabPage1"));
  tab_control->addTab(tab_page1);
  CEGUI::Window* tab_page2 = static_cast<CEGUI::Window*>(p_root_window->getChildElement("TabPage2"));
  tab_control->addTab(tab_page2);
  CEGUI::Window* tab_page3 = static_cast<CEGUI::Window*>(p_root_window->getChildElement("TabPage4"));
  tab_control->addTab(tab_page3);
  CEGUI::Window* tab_page4 = static_cast<CEGUI::Window*>(p_root_window->getChildElement("TabPage3"));
  tab_control->addTab(tab_page4);
  // Subsribing on controller event
  m_game_controller.SubscribeOnValuesChangedEvent([this](EventArguments e)
    {
    this->_UpdateLabels();
    });

  this->_UpdateLabels();
  }

void Menu::_SetButtonCaption(const CEGUI::String &i_button_name, const CEGUI::String &i_button_caption)
  {
  CEGUI::Window *p_root_window = CEGUI::System::getSingleton().getDefaultGUIContext().getRootWindow();
  p_root_window->getChildElementRecursive(i_button_name)->setProperty("Text", i_button_caption);
  }

void Menu::ResetButtonCaptions()
  {
  for(auto button : m_buttons_map)
    {
    _SetButtonCaption(button.first, button.second.button_normal);
    }
  }

void Menu::_UpdateLabels()
  {
  CEGUI::Window *p_root_window = CEGUI::System::getSingleton().getDefaultGUIContext().getRootWindow();
  p_root_window->getChildElementRecursive("MoneyLabel")->setProperty("Text",
    "Money: $" + std::to_string(m_game_controller.GetMoney()) + "  Happiness: " + std::to_string(m_game_controller.GetHappiness() * 100) + "%");
  }

bool Menu::IsMenuHere(double i_mouse_coord_x, double i_mouse_coord_y) const
  {
  // 1. Getting active tab
  CEGUI::Window* p_root = CEGUI::System::getSingleton().getDefaultGUIContext().getRootWindow();
  CEGUI::TabControl* p_tab_control = static_cast<CEGUI::TabControl*>(p_root->getChildElement("TabControl"));
  CEGUI::Window* p_active_tab = nullptr;

  for(size_t id = 0; id < p_tab_control->getChildCount(); ++id)
    {
    if(p_tab_control->getChildAtIdx(id)->isActive())
      {
      p_active_tab = p_tab_control->getChildAtIdx(id);
      break;
      } 
    }

  if(p_active_tab == nullptr)
    return false;

  // 2. Running throught all windows deeper than active window and finding max Y-coord
  double max_y = 0;
  double max_x = 0;
  double min_y = 1e6;
  double min_x = 1e6;
  _RunThrought(min_x, min_y, max_x, max_y, p_active_tab);

  // 3. Checking coordinates
  if(i_mouse_coord_x >= min_x && i_mouse_coord_x <= max_x
    && i_mouse_coord_y >= min_y && i_mouse_coord_y <= max_y)
    return true;
  else
    return false;
  }

